package ch.zhaw.cctd.gui;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.zhaw.cctd.domain.DomainFacade;
import ch.zhaw.cctd.logic.event.LobbyEvent;
import ch.zhaw.cctd.logic.event.LobbyEventListener;
import ch.zhaw.cctd.logic.network.NetworkException;

/**
 * Die Lobby ist der Sammelpunkt aller Spieler, bevor das Spiel losgeht. Es wird
 * erst gestartet, wenn alle User Bereit sind. Der Chat steht schon in der Lobby
 * zur Verfügung.
 * 
 * 
 * @author nathanaelkoch
 * 
 */
public class LobbyGUI implements ActionListener, KeyEventDispatcher {

	private static class LobbyGUILobbyEventListener implements LobbyEventListener {
		private static final Logger logger = LoggerFactory.getLogger(LobbyGUILobbyEventListener.class);
		private static final long serialVersionUID = 2708179749943506997L;
		public JPanel playerListPanel;
		public ArrayList<ReadynessIndicator> readynessIndicators;
		public ArrayList<JLabel> playerLabels;
		public JButton startGame;
		public JButton readyButton;
		public Map<Integer, String> playerList;
		public Map<Integer, Boolean> playerReadyList = new HashMap<Integer, Boolean>();
		public CCTDFrame frame;
		public ChatGUI chat;
		public LobbyMap lobbyMap;
		private Boolean userReady;
		
//		private static class PlayerEnum {
//			public PlayerEnum(int id, String name) {
//				this.name = name;
//				this.id = id;
//			}
//			public final String name;
//			public final int id;
//		}

		public LobbyGUILobbyEventListener(CCTDFrame frame, ArrayList<ReadynessIndicator> readynessIndicators, ArrayList<JLabel> playerLabels) throws RemoteException {
			this.frame = frame;
			playerList = DomainFacade.getPlayerMap();
			this.readynessIndicators = readynessIndicators;
			this.playerLabels = playerLabels;
			int counter = 0;
			for (Map.Entry<Integer, String> entry : playerList.entrySet()) {
				playerReadyList.put(entry.getKey(), false);
				readynessIndicators.get(counter).setVisible(true);
				playerLabels.get(counter).setText("Spieler " + entry.getKey() + ": " + entry.getValue());
				counter++;
			}
			java.rmi.server.UnicastRemoteObject.exportObject(this);
		}

		@Override
		public void userJoined(LobbyEvent event) throws RemoteException {
			logger.warn("userJoined:{} {}", event.user, event.action);
			playerList = DomainFacade.getPlayerMap();
			if (event.action == LobbyEvent.LobbyAction.JOINED) {
				int counter = 0;
				for (Map.Entry<Integer, String> entry : playerList.entrySet()) {
					playerReadyList.put(entry.getKey(), false);
					readynessIndicators.get(counter).setReady(false);
					readynessIndicators.get(counter).setVisible(true);
					playerLabels.get(counter).setText("Spieler "+entry.getKey() + ": " + entry.getValue());
					counter++;
				}
				for (int i = counter; i < BUFFERSIZE; i++) {
					readynessIndicators.get(i).setVisible(false);
					playerLabels.get(counter).setText("");
				}
				logger.trace("User {} Joined", event.user);
			} else if(event.action == LobbyEvent.LobbyAction.LEFT) {
				playerReadyList.remove(event.playerId);
				logger.warn("playerList before: {}", playerList);
				for(int i=0; i < LobbyGUI.BUFFERSIZE; i++) {
					if(playerList.get(i) == null) {
						readynessIndicators.get(i).setVisible(false);
						playerLabels.get(i).setText("");
					}
				}
				logger.warn("playerList after: {}", playerList);
			}
			readyButton.setText("Bereit");
			setUserReady(false);
			playerListPanel.revalidate();
			playerListPanel.repaint();
			lobbyMap.setPlayerList(playerList);
			lobbyMap.repaint();
		}

		@Override
		public void userReady(LobbyEvent event) throws RemoteException {
			playerList = DomainFacade.getPlayerMap();
			int counter = 0;
			boolean allReady = true;
			for (Map.Entry<Integer, String> entry : playerList.entrySet()) {
				Boolean readyEntry = playerReadyList.get(entry.getKey());
				if (entry.getValue().equals(event.user)) {
					readyEntry = !readyEntry;
					playerReadyList.put(entry.getKey(), readyEntry);
				}
				readynessIndicators.get(counter).setReady(readyEntry);
				readynessIndicators.get(counter).setVisible(true);
				playerLabels.get(counter).setText("Spieler " + entry.getKey() + ": " + entry.getValue());
				readynessIndicators.get(counter).repaint();
				if (!playerReadyList.get(entry.getKey())) {
					allReady = false;
				}
				counter++;
			}
			for (int i = counter; i < BUFFERSIZE; i++) {
				readynessIndicators.get(i).setVisible(false);
				playerLabels.get(counter).setText("");
			}
			startGame.setEnabled(allReady);

			playerListPanel.revalidate();
			playerListPanel.repaint();
			logger.trace("Readyness of User {} changed", event.user);
		}

		@Override
		public void gameStart(LobbyEvent event) throws RemoteException {
			frame.playMatch();
			stopEventListener();
		}
		
		public void stopEventListener() {
			try {
				DomainFacade.removeChatEventListener(chat);
				DomainFacade.removeLobbyEventListener(this);
			} catch (NetworkException e) {
				logger.warn("Server already dead", e);
				// Server is already Dead, cant unregister
			}
		}

		public void setUserReady(Boolean userReady) {
			this.userReady = userReady;
		}

		public Boolean getUserReady() {
			return userReady;
		}
	}

	private static final Logger logger = LoggerFactory.getLogger(MatchGUI.class);

	private CCTDFrame frame;
	private LobbyGUILobbyEventListener lobbyListener;
	private JButton readyButton = new JButton("Bereit");
	private JButton startGame = new JButton("Start");
	private ChatGUI chat;
	private JPanel userListPanel;

	private static final int BUFFERSIZE = 16;
	public JPanel playerListPanel;
	public ArrayList<JPanel> playerDisplayPanel;
	public ArrayList<ReadynessIndicator> readynessIndicators;
	public ArrayList<JLabel> playerLabels;
	private LobbyMap lobbyMap;
	private int userId = 0;

	public LobbyGUI(CCTDFrame frame) {
		this.frame = frame;
		KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);
	}

	/**
	 * Wird aufgerufen, um die Lobby zu starten.
	 * 
	 * @throws NetworkException
	 */
	public void start() throws NetworkException {
		JPanel c = (JPanel) frame.getContentPane();
		c.removeAll();
		c.setLayout(new BorderLayout());
		startGame.setEnabled(false);
		playerListPanel = new JPanel(new VerticalFlowLayout());
		playerListPanel.setName("DecoratedPanel");
		readynessIndicators = new ArrayList<ReadynessIndicator>();
		playerDisplayPanel = new ArrayList<JPanel>();
		playerLabels = new ArrayList<JLabel>();
		for (int i = 0; i < BUFFERSIZE; i++) {
			readynessIndicators.add(new ReadynessIndicator());
			playerLabels.add(new JLabel(""));
			JPanel actPanel = new JPanel(new FlowLayout());
			actPanel.add(readynessIndicators.get(i));
			actPanel.add(playerLabels.get(i));
			playerDisplayPanel.add(actPanel);
			playerListPanel.add(playerDisplayPanel.get(i));
		}
		try {
			lobbyListener = new LobbyGUILobbyEventListener(frame, readynessIndicators, playerLabels);
		} catch (RemoteException e1) {
			throw new NetworkException(e1);
		}
		lobbyListener.startGame = startGame;
		lobbyListener.readyButton = readyButton;
		lobbyListener.setUserReady(false);
		lobbyListener.playerListPanel = playerListPanel;
		
		userListPanel = new JPanel(new BorderLayout());
		JPanel buttonPanel = new JPanel(new FlowLayout());
		readyButton.addActionListener(this);
		startGame.addActionListener(this);
		buttonPanel.add(readyButton);
		
		// Adds the Chat to the Lobby
		chat = new ChatGUI();
		
		for(Map.Entry<Integer, String> entry : DomainFacade.getPlayerMap().entrySet()) {
			if(entry.getValue().equals(frame.getUserName())) {
				userId = entry.getKey();
			}
		}
		
		lobbyMap = new LobbyMap(lobbyListener.playerList, userId);
		lobbyListener.lobbyMap = lobbyMap;

		if (frame.isHost()) {
			buttonPanel.add(startGame);
		}
		userListPanel.add(buttonPanel, BorderLayout.NORTH);
		userListPanel.add(playerListPanel, BorderLayout.WEST);
		userListPanel.add(lobbyMap, BorderLayout.CENTER);
		
		c.add(userListPanel, BorderLayout.CENTER);
		lobbyListener.chat = chat;
		
		try {
			DomainFacade.registerChatEventListener(chat);
			DomainFacade.registerLobbyEventListener(lobbyListener);
		} catch (NetworkException e) {
			if (e.isFatalError()) {
				frame.quitGame();
			}
			logger.error(e.getMessage());
		}

		c.add(chat, BorderLayout.SOUTH);

		c.repaint();

		frame.setContentPane(c);
	}

	public void setReady() {
		if (!lobbyListener.getUserReady()) {
			readyButton.setText("Nicht bereit");
			DomainFacade.lobbySendUserIsReady();
		} else {
			readyButton.setText("Bereit");
			DomainFacade.lobbySendUserIsNotReady();
		}
		lobbyListener.setUserReady(!lobbyListener.getUserReady());
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == readyButton) {
			setReady();
		} else if (e.getSource().equals(startGame)) {
			DomainFacade.lobbySendGameStart();
		}
	}

	public void stopEventListener() {
		lobbyListener.stopEventListener();
	}
	
	@Override
	public boolean dispatchKeyEvent(KeyEvent e) {
		if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
			logger.trace("Menu Window opened");
			frame.getMenuGUI().display();
		}
		return false;
	}

}
